// 知识点1：类模版的定义：与函数模版不同的是，编译器不会为类模版推断模版参数类型，所以我们在使用类模版时，需要显式地指出元素的类型，在其定义中，模版参数可以当作类型使用，用来表示类保存的元素的类型：比如说类中保存了一个T类型的vector变量

// 知识点2：我们可以将类中元素访问操作的返回类型定义为T&，也就是模版参数的引用，在被实例化之后，T会被替换为特定的模版实参类型

// 知识点3：一个类模版的每个实例都会形成一个独立的类，与其他实例化的类之间并没有特殊的访问权限

// 知识点4：实例化时，编译器会重写类模版，将模版参数替换为给定的模版实参

// 知识点5：在一个类模版中使用另一种模版，通常不会将一个实际的类型（如：int）当作其模版实参，而将模版自己的参数当作被使用模版的实参，比如在一个模版类中使用的vector和shared_ptr都是类模版，我们在使用时，会将T作为模版实参传递给他们

// 知识点6：无论何时使用模版都必须提供模版实参

// 知识点7：我们可以在类模版的内部或者外部对类模版的成员函数进行定义，定义在类模版内的成员函数被隐式的声明为inline函数

// 知识点8：由于类模版的每个实例都有自己版本的成员函数，因此类模版的成员函数具有和模版相同的模版参数，因此，在定义类模版之外的成员函数必须以关键词template开始，后接类模版实参列表（在其返回类型之后还需要加类名和<>参数列表）

#ifndef CLASS_TYPENAME_H
#define CLASS_TYPENAME_H
#include <string>
using namespace std;
template <typename T>
class Blob {
 public:
  Blob();
  void check(size_t, const string&) const;

 private:
  shared_ptr<vector<T>> data;
};

template <typename T>
Blob<T>::Blob() : data(make_shared(vector<T>)) {}  //类外定义构造函数
template <typename T>
//类外定义成员函数
void Blob<T>::check(size_t i, const string& msg) const {
  if (i > data->size()) {
    throw out_of_range(msg);
  }
}
#endif  // CLASS_TYPENAME_H
// 知识点9：类模版的构造函数，无需返回类型，其他和普通成员函数一样

// 知识点10：类模版的成员函数实例化：只有在程序使用它时才会被实例化，即使其类模版已经被实例化

// 知识点11：在类模版自己的作用域中（即类内），我们可以直接使用模版名而不提供实参（不需要<>这个东西了）而在类外则需要指定模版参数（返回值是模版的类型）

// 知识点12：当一个类模版包含一个非模版友元，则友元被授权可以访问所有的模版实例，如果友元自身是模版，类可以授权给所有友元模版实例，也可以只授予给定实例。如想要所有实例称为友元，友元声明中必须使用与类模版不同的模版参数

// 知识点13：C++11新标准：可以将模版参数类型声明为友元，比如int将称为Blob<int>的友元

// 知识点14：C++11新标准：我们可以定义一个typedef来引用实例化的类，还可以使用using来声明类型别名

// typedef Blob<string> Blob;//之后使用Blob就是string类型的了

// template <typename T> using twin = Blob<T>;//twin代指Blob
// 知识点15：类模版的static成员：每一个类模版的实例都有自己的static成员实例，但对于给定的类型，该static成员实例共享

// 知识点16：类模版的static成员有且仅有一个定义

// 答案：由知识点11可知，在List类内，使用模版名不需要再加参数列表，而ListItem使用时必须加上<>模版参数列表
